/**
 * A date picker. This class is used by the Ext.form.field.Date field to allow
 * browsing and selection of valid dates in a popup next to the field, but may
 * also be used with other components.
 * 
 * Typically you will need to implement a handler function to be notified when
 * the user chooses a date from the picker; you can register the handme.hourBtn
 * ler using the {@link #select} event, or by implementing the {@link #handler}
 * method.
 * 
 * By default the user will be allowed to pick any date; this can be changed by
 * using the {@link #minDate}, {@link #maxDate}, {@link #disabledDays},
 * {@link #disabledDatesRE}, and/or {@link #disabledDates} configs.
 * 
 * All the string values documented below may be overridden by including an Ext
 * locale file in your page.
 * 
 * @example Ext.create('Ext.panel.Panel', { title: 'Choose a future date:',
 *          width: 200, bodyPadding: 10, renderTo: Ext.getBody(), items: [{
 *          xtype: 'datepicker', minDate: new Date(), handler: function(picker,
 *          date) { // do something with the selected date } }] });
 *         
 *          由jfok 于 2016年07月14日制作完成。
 *          
 *          jfok1972@qq.com
 *          https://blog.csdn.net/jfok/article/details/51911719
 */
Ext.define('appfe.ux.picker.DateTime',
	{
		extend: 'Ext.Component',
		requires: ['Ext.XTemplate', 'Ext.button.Button',
			'Ext.button.Split', 'Ext.util.ClickRepeater', 'Ext.util.KeyNav',
			'Ext.fx.Manager', 'Ext.picker.Month'],
		alias: 'widget.datetimepicker',
		alternateClassName: 'Ext.DateTimePicker',
		okText: '确定',
		// <locale>
		/**
		 * @cfg {String} todayText The text to display on the button that
		 *      selects the current date
		 */
		//todayText: 'Today',
		todayText: "现在",
		// </locale>

		// <locale>
		/**
		 * @cfg {String} ariaTitle The text to display for the aria title
		 */
		//ariaTitle: 'Date Picker: {0}',
		ariaTitle: '{0}',
		// </locale>

		// <locale>
		/**
		 * @cfg {String} ariaTitleDateFormat The date format to display for
		 *      the current value in the {@link #ariaTitle}
		 */
		//ariaTitleDateFormat: 'F d',
		ariaTitleDateFormat: 'Y\u5e74m\u6708d\u65e5',
		// </locale>

		/**
		 * @cfg {Function} handler Optional. A function that will handle the
		 *      select event of this picker. The handler is passed the
		 *      following parameters: - `picker` : Ext.picker.Date
		 * 
		 * This Date picker. - `date` : Date
		 * 
		 * The selected date.
		 */

		/**
		 * @cfg {Object} scope The scope (`this` reference) in which the `{@link #handler}`
		 *      function will be called.
		 * 
		 * Defaults to this DatePicker instance.
		 */

		// <locale>
		/**
		 * @cfg {String} todayTip A string used to format the message for
		 *      displaying in a tooltip over the button that selects the
		 *      current date. The `{0}` token in string is replaced by today's
		 *      date.
		 */
		//todayTip: '{0} (Spacebar)',
		todayTip: "{0}",
		// </locale>
        format: "y年m月d日",
		// <locale>
		/**
		 * @cfg {String} minText The error text to display if the minDate
		 *      validation fails.
		 */
		//minText: 'This date is before the minimum date',
		minText: "日期必须大于最小允许日期",
		// </locale>

		// <locale>
		/**
		 * @cfg {String} ariaMinText The text that will be announced by
		 *      Assistive Technologies such as screen readers when user is
		 *      navigating to the cell which date is less than
		 *      {@link #minDate}.
		 */
		ariaMinText: "This date is before the minimum date",
		// </locale>

		// <locale>
		/**
		 * @cfg {String} maxText The error text to display if the maxDate
		 *      validation fails.
		 */
		//maxText: 'This date is after the maximum date',
		maxText: "日期必须小于最大允许日期",
		// </locale>

		// <locale>
		/**
		 * @cfg {String} ariaMaxText The text that will be announced by
		 *      Assistive Technologies such as screen readers when user is
		 *      navigating to the cell which date is later than
		 *      {@link #maxDate}.
		 */
		ariaMaxText: "This date is after the maximum date",
		// </locale>

		/**
		 * @cfg {String} format
		 * 
		 * 
		 *  The default date format string which can be
		 *      overriden for localization support. The format must be valid
		 *      according to {@link Ext.Date#parse} (defaults to
		 *      {@link Ext.Date#defaultFormat}).
		 */

		// <locale>
		/**
		 * @cfg {String} disabledDaysText The tooltip to display when the date
		 *      falls on a disabled day.
		 */
		//disabledDaysText: 'Disabled',
		disabledDaysText: "",
		// </locale>

		// <locale>
		/**
		 * @cfg {String} ariaDisabledDaysText The text that Assistive
		 *      Technologies such as screen readers will announce when the
		 *      date falls on a disabled day of week.
		 */
		ariaDisabledDaysText: "This day of week is disabled",
		// </locale>

		// <locale>
		/**
		 * @cfg {String} disabledDatesText The tooltip text to display when
		 *      the date falls on a disabled date.
		 */
		//disabledDatesText: 'Disabled',
		disabledDatesText: "",
		// </locale>

		// <locale>
		/**
		 * @cfg {String} ariaDisabledDatesText The text that Assistive
		 *      Technologies such as screen readers will announce when the
		 *      date falls on a disabled date.
		 */
		ariaDisabledDatesText: "This date is disabled",

		// </locale>
		/**
		 * @cfg {String[]} monthNames An array of textual month names which
		 *      can be overriden for localization support (defaults to
		 *      Ext.Date.monthNames)
		 * @deprecated This config is deprecated. In future the month names
		 *             will be retrieved from {@link Ext.Date}
		 */

		/**
		 * @cfg {String[]} dayNames An array of textual day names which can be
		 *      overriden for localization support (defaults to
		 *      Ext.Date.dayNames)
		 * @deprecated This config is deprecated. In future the day names will
		 *             be retrieved from {@link Ext.Date}
		 */

		// <locale>
		/**
		 * @cfg {String} nextText The next month navigation button tooltip
		 */
		//nextText: 'Next Month (Control+Right)',
		nextText: '下个月 (Ctrl+Right)',
		// </locale>

		// <locale>
		/**
		 * @cfg {String} prevText The previous month navigation button tooltip
		 */
		//prevText: 'Previous Month (Control+Left)',
		prevText: '上个月 (Ctrl+Left)',
		// </locale>

		// <locale>
		/**
		 * @cfg {String} monthYearText The header month selector tooltip
		 */
		//monthYearText: 'Choose a month (Control+Up/Down to move years)',
		monthYearText: '选择一个月 (Ctrl+Up/Down 来改变年份)',
		// </locale>

		// <locale>
		/**
		 * @cfg {String} monthYearFormat The date format for the header month
		 */
		//monthYearFormat: 'F Y',
		monthYearFormat: 'Y\u5e74m\u6708',
		// </locale>

		// <locale>
		/**
		 * @cfg {Number} [startDay=undefined] Day index at which the week
		 *      should begin, 0-based.
		 * 
		 * Defaults to `0` (Sunday).
		 */
		startDay: 0,
		// </locale>

		// <locale>
		/**
		 * @cfg {Boolean} showToday False to hide the footer area containing
		 *      the Today button and disable the keyboard handler for spacebar
		 *      that selects the current date.
		 */
		showToday: true,
		// </locale>

		/**
		 * @cfg {Date} [minDate=null] Minimum allowable date (JavaScript date
		 *      object)
		 */

		/**
		 * @cfg {Date} [maxDate=null] Maximum allowable date (JavaScript date
		 *      object)
		 */

		/**
		 * @cfg {Number[]} [disabledDays=null] An array of days to disable,
		 *      0-based. For example, [0, 6] disables Sunday and Saturday.
		 */

		/**
		 * @cfg {RegExp} [disabledDatesRE=null] JavaScript regular expression
		 *      used to disable a pattern of dates. The {@link #disabledDates}
		 *      config will generate this regex internally, but if you specify
		 *      disabledDatesRE it will take precedence over the disabledDates
		 *      value.
		 */

		/**
		 * @cfg {String[]} disabledDates An array of 'dates' to disable, as
		 *      strings. These strings will be used to build a dynamic regular
		 *      expression so they are very powerful. Some examples: -
		 *      ['03/08/2003', '09/16/2003'] would disable those exact dates -
		 *      ['03/08', '09/16'] would disable those days for every year -
		 *      ['^03/08'] would only match the beginning (useful if you are
		 *      using short years) - ['03/../2006'] would disable every day in
		 *      March 2006 - ['^03'] would disable every day in every March
		 * 
		 * Note that the format of the dates included in the array should
		 * exactly match the {@link #format} config. In order to support
		 * regular expressions, if you are using a date format that has '.' in
		 * it, you will have to escape the dot when restricting dates. For
		 * example: ['03\\.08\\.03'].
		 */

		/**
		 * @cfg {Boolean} disableAnim True to disable animations when showing
		 *      the month picker.
		 */
		disableAnim: false,

		/**
		 * @cfg {String} [baseCls='x-datepicker'] The base CSS class to apply
		 *      to this components element.
		 */
		baseCls: Ext.baseCSSPrefix + 'datepicker',

		/**
		 * @cfg {String} [selectedCls='x-datepicker-selected'] The class to
		 *      apply to the selected cell.
		 */

		/**
		 * @cfg {String} [disabledCellCls='x-datepicker-disabled'] The class
		 *      to apply to disabled cells.
		 */

		// <locale>
		/**
		 * @cfg {String} longDayFormat The format for displaying a date in a
		 *      longer format.
		 */
		//longDayFormat: 'F d, Y',
		longDayFormat: 'Y\u5e74m\u6708d\u65e5',
		// </locale>

		/**
		 * @cfg {Object} keyNavConfig Specifies optional custom key event
		 *      handlers for the {@link Ext.util.KeyNav} attached to this date
		 *      picker. Must conform to the config format recognized by the
		 *      {@link Ext.util.KeyNav} constructor. Handlers specified in
		 *      this object will replace default handlers of the same name.
		 */

		/**
		 * @cfg {String} The {@link Ext.button.Button#ui} to use for the date
		 *      picker's footer buttons.
		 */
		footerButtonUI: 'default',

		isDatePicker: true,
		alignOnScroll: false,

		ariaRole: 'region',
		focusable: true,

		childEls: ['innerEl', 'eventEl', 'prevEl', 'nextEl', 'middleBtnEl',
			'footerEl'],

		border: true,

		/**
		 * @cfg
		 * @inheritdoc
		 */
		renderTpl: [
			'<div id="{id}-innerEl" data-ref="innerEl" role="presentation">',
			'<div class="{baseCls}-header">',
			'<div id="{id}-prevEl" data-ref="prevEl" class="{baseCls}-prev {baseCls}-arrow" role="presentation" title="{prevText}"></div>',
			'<div id="{id}-middleBtnEl" data-ref="middleBtnEl" class="{baseCls}-month" role="heading">{%this.renderMonthBtn(values, out)%}</div>',
			'<div id="{id}-nextEl" data-ref="nextEl" class="{baseCls}-next {baseCls}-arrow" role="presentation" title="{nextText}"></div>',
			'</div>',
			'<table role="grid" id="{id}-eventEl" data-ref="eventEl" class="{baseCls}-inner" cellspacing="0" tabindex="0" aria-readonly="true">',
			'<thead>',
			'<tr role="row">',
			'<tpl for="dayNames">',
			'<th role="columnheader" class="{parent.baseCls}-column-header" aria-label="{.}">',
			'<div role="presentation" class="{parent.baseCls}-column-header-inner">{.:this.firstInitial}</div>',
			'</th>',
			'</tpl>',
			'</tr>',
			'</thead>',
			'<tbody>',
			'<tr role="row">',
			'<tpl for="days">',
			'{#:this.isEndOfWeek}',
			'<td role="gridcell">',
			'<div hidefocus="on" class="{parent.baseCls}-date"></div>',
			'</td>',
			'</tpl>',
			'</tr>',
			'</tbody>',
			'</table>',

			// 指定时分秒渲染框架
			'<table id="{id}-timeEl" style="table-layout:auto;margin:0 auto;width:90%;text-align:center;" class="x-datepicker-inner" cellspacing="0">',
			'<tbody><tr>',
			'<td width="30%">{%this.renderHourField(values,out)%}</td>',
			'<td width="35%">{%this.renderMinuteField(values,out)%}</td>',
			'<td width="35%">{%this.renderSecondField(values,out)%}</td>',
			'</tr></tbody>',
			'</table>',
			// 上面是新增的

			'<tpl if="showToday">',
			'<div id="{id}-footerEl" data-ref="footerEl" role="presentation" class="{baseCls}-footer">',
			// 增加了确认按钮
			'{%this.renderOkBtn(values, out)%}',
			// 上面是新增的
			'{%this.renderTodayBtn(values, out)%}',
			// Add by wonght
			'{%this.renderCleanBtn(values, out)%}',
			'</div>',
			'</tpl>',
			// These elements are used with Assistive Technologies such as
			// screen readers
			'<div id="{id}-todayText" class="' + Ext.baseCSSPrefix
			+ 'hidden-clip">{todayText}.</div>',
			'<div id="{id}-ariaMinText" class="' + Ext.baseCSSPrefix
			+ 'hidden-clip">{ariaMinText}.</div>',
			'<div id="{id}-ariaMaxText" class="' + Ext.baseCSSPrefix
			+ 'hidden-clip">{ariaMaxText}.</div>',
			'<div id="{id}-ariaDisabledDaysText" class="' + Ext.baseCSSPrefix
			+ 'hidden-clip">{ariaDisabledDaysText}.</div>',
			'<div id="{id}-ariaDisabledDatesText" class="'
			+ Ext.baseCSSPrefix
			+ 'hidden-clip">{ariaDisabledDatesText}.</div>',
			'</div>',
			{
				firstInitial: function (value) {
					return Ext.picker.Date.prototype.getDayInitial(value);
				},
				isEndOfWeek: function (value) {
					// convert from 1 based index to 0 based
					// by decrementing value once.
					value--;
					var end = value % 7 === 0 && value !== 0;
					return end ? '</tr><tr role="row">' : '';
				},
				renderTodayBtn: function (values, out) {
					Ext.DomHelper.generateMarkup(values.$comp.todayBtn
						.getRenderTree(), out);
				},
				renderMonthBtn: function (values, out) {
					Ext.DomHelper.generateMarkup(values.$comp.monthBtn
						.getRenderTree(), out);
				},
				// 指定渲染方法调用
				renderHourField: function (values, out) {
					Ext.DomHelper.generateMarkup(values.$comp.hourField
						.getRenderTree(), out);
				},
				renderMinuteField: function (values, out) {
					Ext.DomHelper.generateMarkup(values.$comp.minuteField
						.getRenderTree(), out);
				},
				renderSecondField: function (values, out) {
					Ext.DomHelper.generateMarkup(values.$comp.secondField
						.getRenderTree(), out);
				},
				renderOkBtn: function (values, out) {
					Ext.DomHelper.generateMarkup(values.$comp.okBtn
						.getRenderTree(), out);
				},
				//add by wonght
				renderCleanBtn: function (values, out) {
					Ext.DomHelper.generateMarkup(values.$comp.cleanBtn
						.getRenderTree(), out);
				}
				// 以上四个函数是新增的
			}],

		// Default value used to initialise each date in the DatePicker.
		// __Note:__ 12 noon was chosen because it steers well clear of all
		// DST timezone changes.
		initHour: 12, // 24-hour format

		numDays: 42,

		/**
		 * @event select Fires when a date is selected
		 * @param {Ext.picker.Date}
		 *          this DatePicker
		 * @param {Date}
		 *          date The selected date
		 */

		initComponent: function () {
			var me = this;

			me.selectedCls = me.baseCls + '-selected';
			me.disabledCellCls = me.baseCls + '-disabled';
			me.prevCls = me.baseCls + '-prevday';
			me.activeCls = me.baseCls + '-active';
			me.cellCls = me.baseCls + '-cell';
			me.nextCls = me.baseCls + '-prevday';
			me.todayCls = me.baseCls + '-today';

			if (!me.format) {
				me.format = Ext.Date.defaultFormat;
			}
			if (!me.dayNames) {
				me.dayNames = Ext.Date.dayNames;
			}
			me.dayNames = me.dayNames.slice(me.startDay).concat(
				me.dayNames.slice(0, me.startDay));

			me.callParent();

			// var dateStr = Ext.Date.format(new Date(), 'Y-m-d') + ' 00:00:00';
			// var currdate = Ext.Date.parse(dateStr, 'Y-m-d H:i:s', true);
			// console.debug('dateStr', dateStr);
			// console.debug('currdate', currdate);
			// console.debug('me.value', me.value);
			// me.value = me.value ? (me.value) : (currdate);
			me.value = me.value ? (me.value) : (new Date());
			me.initDisabledDays();
		},

		// Keep the tree structure correct for Ext.form.field.Picker input
		// fields which poke a 'pickerField' reference down into their pop-up
		// pickers.
		getRefOwner: function () {
			return this.pickerField || this.callParent();
		},

		getRefItems: function () {
			var results = [], monthBtn = this.monthBtn, todayBtn = this.todayBtn;

			if (monthBtn) {
				results.push(monthBtn);
			}

			if (todayBtn) {
				results.push(todayBtn);
			}
			return results;
		},

		beforeRender: function () {
			/*
			 * days array for looping through 6 full weeks (6 weeks * 7 days)
			 * Note that we explicitly force the size here so the template
			 * creates all the appropriate cells.
			 */
			var me = this, encode = Ext.String.htmlEncode, days = new Array(
				me.numDays), today = Ext.Date.format(new Date(), me.format);

			if (me.padding && !me.width) {
				me.cacheWidth();
			}

			// 自己加的
			me.hourField = new Ext.form.field.Number({
				ownerCt: me,
				ownerLayout: me.getComponentLayout(),
				minValue: 0,
				maxValue: 23,
				step: 1,
				width: '100%',
				enableKeyEvents: true,
				listeners: {
					specialkey: function (field, e) {
						if (e.getKey() == e.ENTER) {
							e.stopEvent();
							me.minuteField.focus(true);
						}
					}
				}
			});
			me.minuteField = new Ext.form.field.Number({
				ownerCt: me,
				ownerLayout: me.getComponentLayout(),
				minValue: 0,
				maxValue: 59,
				step: 1,
				width: '100%',
				labelWidth: 10,
				fieldLabel: '&nbsp;',
				enableKeyEvents: true,
				listeners: {
					specialkey: function (field, e) {
						if (e.getKey() == e.ENTER) {
							e.stopEvent();
							me.secondField.focus(true);
						}
					}
				}
			});
			me.secondField = new Ext.form.field.Number({
				ownerCt: me,
				ownerLayout: me.getComponentLayout(),
				minValue: 0,
				maxValue: 59,
				step: 1,
				width: '100%',
				labelWidth: 10,
				fieldLabel: '&nbsp;',
				enableKeyEvents: true,
				listeners: {
					specialkey: function (field, e) {
						if (e.getKey() == e.ENTER) {
							e.stopEvent();
							me.okBtn.focus(true);
						}
					}
				}
			});
			// 自己加的

			me.monthBtn = new Ext.button.Split({
				ownerCt: me,
				ownerLayout: me.getComponentLayout(),
				text: '',
				tooltip: me.monthYearText,
				tabIndex: -1,
				ariaRole: 'presentation',
				listeners: {
					click: me.doShowMonthPicker,
					arrowclick: me.doShowMonthPicker,
					scope: me
				}
			});

			if (me.showToday) {

				// 自己加的
				me.okBtn = new Ext.button.Button({
					ui: me.footerButtonUI,
					ownerCt: me,
					ownerLayout: me.getComponentLayout(),
					text: me.okText,
					handler: me.okHandler, // 确认按钮的事件委托
					scope: me
				});
				// 自己加的

				me.todayBtn = new Ext.button.Button({
					ui: me.footerButtonUI,
					ownerCt: me,
					ownerLayout: me.getComponentLayout(),
					text: Ext.String.format(me.todayText, today),
					tooltip: Ext.String.format(me.todayTip, today),
					tooltipType: 'title',
					tabIndex: -1,
					ariaRole: 'presentation',
					handler: me.selectToday,
					scope: me
				});
				//add by wonght
				me.cleanBtn = new Ext.button.Button({
					ui: me.footerButtonUI,
					ownerCt: me,
					ownerLayout: me.getComponentLayout(),
					text: "清空",
					handler: me.cleanHandler, // 确认按钮的事件委托
					scope: me
				});
			}

			me.callParent();

			Ext.applyIf(me, {
				renderData: {}
			});

			Ext.apply(me.renderData, {
				dayNames: me.dayNames,
				showToday: me.showToday,
				prevText: encode(me.prevText),
				nextText: encode(me.nextText),
				todayText: encode(me.todayText),
				ariaMinText: encode(me.ariaMinText),
				ariaMaxText: encode(me.ariaMaxText),
				ariaDisabledDaysText: encode(me.ariaDisabledDaysText),
				ariaDisabledDatesText: encode(me.ariaDisabledDatesText),
				days: days
			});

			me.protoEl.unselectable();
		},

		/**
		 * 确认 按钮触发的调用
		 */
		okHandler: function () {
			var me = this, btn = me.okBtn;
			if (btn && !btn.disabled) {
				me.setValue(this.getValue());
				me.fireEvent('select', me, me.value);
				me.onSelect();
			}
		},
		// add by wonght	
		cleanHandler: function () {
			var me = this, btn = me.cleanBtn;
			if (btn && !btn.disabled) {
				me.setValue('');
				me.fireEvent('select', me, '');
				me.onSelect();
			}
		},

		cacheWidth: function () {
			var me = this, padding = me.parseBox(me.padding), widthEl = Ext
				.getBody().createChild({
					cls: me.baseCls + ' ' + me.borderBoxCls,
					style: 'position:absolute;top:-1000px;left:-1000px;'
				});

			me.self.prototype.width = widthEl.getWidth() + padding.left
				+ padding.right;
			widthEl.destroy();
		},

		/**
		 * @inheritdoc
		 * @private
		 */
		onRender: function (container, position) {
			var me = this;

			me.callParent(arguments);

			me.cells = me.eventEl.select('tbody td');
			me.textNodes = me.eventEl.query('tbody td div');

			me.eventEl.set({
				'aria-labelledby': me.monthBtn.id
			});

			me.mon(me.eventEl, {
				scope: me,
				mousewheel: me.handleMouseWheel,
				click: {
					fn: me.handleDateClick,
					delegate: 'div.' + me.baseCls + '-date'
				}
			});

		},

		/**
		 * @inheritdoc
		 * @private
		 */
		initEvents: function () {
			var me = this;

			me.callParent();

			// If we're part of a date field, don't allow us to focus on
			// mousedown,
			// the field will handle that. If we are standalone, then allow the
			// default
			// behaviour to occur to receive focus
			if (me.pickerField) {
				me.el.on('mousedown', me.onMouseDown, me);
			}

			// Month button is pointer interactive only, it should not be
			// allowed to focus.
			me.monthBtn.el.on('mousedown', me.onMouseDown, me);

			me.prevRepeater = new Ext.util.ClickRepeater(me.prevEl, {
				handler: me.showPrevMonth,
				scope: me,
				mousedownStopEvent: true
			});

			me.nextRepeater = new Ext.util.ClickRepeater(me.nextEl, {
				handler: me.showNextMonth,
				scope: me,
				mousedownStopEvent: true
			});

			me.keyNav = new Ext.util.KeyNav(
				me.eventEl,
				Ext
					.apply(
						{
							scope: me,

							left: function (e) {
								if (e.ctrlKey) {
									this.showPrevMonth();
								} else {
									this.update(Ext.Date.add(this.activeDate,
										Ext.Date.DAY, -1));
								}

								// We need to prevent default to avoid scrolling the
								// nearest container
								// which in case of a floating Date picker will be
								// the document body.
								// This applies to all navigation keys.
								e.preventDefault();
							},

							right: function (e) {
								if (e.ctrlKey) {
									this.showNextMonth();
								} else {
									this.update(Ext.Date.add(this.activeDate,
										Ext.Date.DAY, 1));
								}

								e.preventDefault();
							},

							up: function (e) {
								// This is non-standard behavior kept for backward
								// compatibility.
								// Ctrl-PageUp is reverse to this and it should be
								// used instead.
								if (e.ctrlKey) {
									this.showNextYear();
								} else {
									this.update(Ext.Date.add(this.activeDate,
										Ext.Date.DAY, -7));
								}

								e.preventDefault();
							},

							down: function (e) {
								// This is non-standard behavior kept for backward
								// compatibility.
								// Ctrl-PageDown is reverse to this and it should be
								// used instead.
								if (e.ctrlKey) {
									this.showPrevYear();
								} else {
									this.update(Ext.Date.add(this.activeDate,
										Ext.Date.DAY, 7));
								}

								e.preventDefault();
							},

							pageUp: function (e) {
								if (e.ctrlKey) {
									this.showPrevYear();
								} else {
									this.showPrevMonth();
								}

								e.preventDefault();
							},

							pageDown: function (e) {
								if (e.ctrlKey) {
									this.showNextYear();
								} else {
									this.showNextMonth();
								}

								e.preventDefault();
							},

							home: function (e) {
								this.update(Ext.Date
									.getFirstDateOfMonth(this.activeDate));

								e.preventDefault();
							},

							end: function (e) {
								this.update(Ext.Date
									.getLastDateOfMonth(this.activeDate));

								e.preventDefault();
							},

							tab: function (e) {
								// When the picker is floating and attached to an
								// input field, its
								// 'select' handler will focus the inputEl so when
								// navigation happens
								// it does so as if the input field was focused all
								// the time.
								// This is the desired behavior and we try not to
								// interfere with it
								// in the picker itself, see below.
								this.handleTabKey(e);

								// Allow default behaviour of TAB - it MUST be
								// allowed to navigate.
								return true;
							},

							enter: function (e) {
								this.handleDateClick(e, this.activeCell.firstChild);
							},

							space: function (e) {
								var me = this, pickerField = me.pickerField, startValue, value, pickerValue;

								me.setValue(new Date(
									me.activeCell.firstChild.dateValue));

								if (pickerField) {
									startValue = me.startValue;
									value = me.value;
									pickerValue = pickerField.getValue();

									if (pickerValue && startValue
										&& pickerValue.getTime() === value.getTime()) {
										pickerField.setValue(startValue);
									} else {
										pickerField.setValue(value);
									}
								}

								// Space key causes scrolling, too :(
								e.preventDefault();
							}
						}, me.keyNavConfig));

			if (me.disabled) {
				me.syncDisabled(true, true);
			}

			me.update(me.value);
		},

		onMouseDown: function (e) {
			if (e.target.id.indexOf("numberfield") !== 0)
				e.preventDefault();
		},

		handleTabKey: function (e) {
			var me = this, t = me.getSelectedDate(me.activeDate), handler = me.handler;

			// The following code is like handleDateClick without the
			// e.stopEvent()
			if (!me.disabled && t.dateValue
				&& !Ext.fly(t.parentNode).hasCls(me.disabledCellCls)) {
				me.setValue(new Date(t.dateValue));
				me.fireEvent('select', me, me.value);
				if (handler) {
					handler.call(me.scope || me, me, me.value);
				}
				me.onSelect();
			}
			// Even if the above condition is not met we have to let the field
			// know
			// that we're tabbing out - that's user action we can do nothing
			// about
			else {
				me.fireEventArgs('tabout', [me]);
			}
		},

		getSelectedDate: function (date) {
			var me = this, t = date.getTime(), cells = me.cells, cls = me.selectedCls, cellItems = cells.elements, cLen = cellItems.length, cell, c;

			cells.removeCls(cls);

			for (c = 0; c < cLen; c++) {
				cell = cellItems[c].firstChild;
				if (cell.dateValue === t) {
					return cell;
				}
			}
			return null;
		},

		/**
		 * Setup the disabled dates regex based on config options
		 * 
		 * @private
		 */
		initDisabledDays: function () {
			var me = this, dd = me.disabledDates, re = '(?:', len, d, dLen, dI;

			if (!me.disabledDatesRE && dd) {
				len = dd.length - 1;

				dLen = dd.length;

				for (d = 0; d < dLen; d++) {
					dI = dd[d];

					re += Ext.isDate(dI) ? '^'
						+ Ext.String
							.escapeRegex(Ext.Date.dateFormat(dI, me.format)) + '$'
						: dI;
					if (d !== len) {
						re += '|';
					}
				}

				me.disabledDatesRE = new RegExp(re + ')');
			}
		},

		/**
		 * Replaces any existing disabled dates with new values and refreshes
		 * the DatePicker.
		 * 
		 * @param {String[]/RegExp}
		 *          disabledDates An array of date strings (see the
		 *          {@link #disabledDates} config for details on supported
		 *          values), or a JavaScript regular expression used to
		 *          disable a pattern of dates.
		 * @return {Ext.picker.Date} this
		 */
		setDisabledDates: function (dd) {
			var me = this;

			if (Ext.isArray(dd)) {
				me.disabledDates = dd;
				me.disabledDatesRE = null;
			} else {
				me.disabledDatesRE = dd;
			}
			me.initDisabledDays();
			me.update(me.value, true);
			return me;
		},

		/**
		 * Replaces any existing disabled days (by index, 0-6) with new values
		 * and refreshes the DatePicker.
		 * 
		 * @param {Number[]}
		 *          disabledDays An array of disabled day indexes. See the
		 *          {@link #disabledDays} config for details on supported
		 *          values.
		 * @return {Ext.picker.Date} this
		 */
		setDisabledDays: function (dd) {
			this.disabledDays = dd;
			return this.update(this.value, true);
		},

		/**
		 * Replaces any existing {@link #minDate} with the new value and
		 * refreshes the DatePicker.
		 * 
		 * @param {Date}
		 *          value The minimum date that can be selected
		 * @return {Ext.picker.Date} this
		 */
		setMinDate: function (dt) {
			this.minDate = dt;
			return this.update(this.value, true);
		},

		/**
		 * Replaces any existing {@link #maxDate} with the new value and
		 * refreshes the DatePicker.
		 * 
		 * @param {Date}
		 *          value The maximum date that can be selected
		 * @return {Ext.picker.Date} this
		 */
		setMaxDate: function (dt) {
			this.maxDate = dt;
			return this.update(this.value, true);
		},

		/**
		 * Sets the value of the date field
		 * 
		 * @param {Date}
		 *          value The date to set
		 * @return {Ext.picker.Date} this
		 */
		setValue: function (value, isfixed) {
			// If passed a null value just pass in a new date object.

			var me = this;
			// 这里一定要用 Ext.Date.clone ，不然不会触发 isDirty事件
			this.value = Ext.Date.clone(value || new Date());

			if (isfixed !== true) {
				this.value.setHours(me.hourField.getValue());
				this.value.setMinutes(me.minuteField.getValue());
				this.value.setSeconds(me.secondField.getValue());
			}

			return this.update(this.value);
		},

		/**
		 * Gets the current selected value of the date field
		 * 
		 * @return {Date} The selected date
		 */
		getValue: function () {
			return this.value;
		},

		// <locale type="function">
		/**
		 * Gets a single character to represent the day of the week
		 * 
		 * @return {String} The character
		 */
		//getDayInitial: function (value) {
		//	return value.substr(0, 1);
		//},
		getDayInitial: function (value) {
 			// Grab the last character
 			return value.substr(value.length - 1);
 		},
		// </locale>

		/**
		 * @inheritdoc
		 * @private
		 */
		onEnable: function () {
			var me = this;

			me.callParent();
			me.syncDisabled(false, true);
			me.update(me.activeDate);

		},

		/**
		 * @inheritdoc
		 * @private
		 */
		onShow: function () {
			var me = this;

			me.callParent();
			me.syncDisabled(false);
			if (me.pickerField) {
				me.startValue = me.pickerField.getValue();
			}
		},

		/**
		 * @inheritdoc
		 * @private
		 */
		onHide: function () {
			this.callParent();
			this.syncDisabled(true);
		},

		/**
		 * @inheritdoc
		 * @private
		 */
		onDisable: function () {
			this.callParent();
			this.syncDisabled(true, true);
		},

		/**
		 * Get the current active date.
		 * 
		 * @private
		 * @return {Date} The active date
		 */
		getActive: function () {
			return this.activeDate || this.value;
		},

		/**
		 * Run any animation required to hide/show the month picker.
		 * 
		 * @private
		 * @param {Boolean}
		 *          isHide True if it's a hide operation
		 */
		runAnimation: function (isHide) {
			var picker = this.monthPicker, options = {
				duration: 200,
				callback: function () {
					picker.setVisible(!isHide);
				}
			};

			if (isHide) {
				picker.el.slideOut('t', options);
			} else {
				picker.el.slideIn('t', options);
			}
		},

		/**
		 * Hides the month picker, if it's visible.
		 * 
		 * @param {Boolean}
		 *          [animate] Indicates whether to animate this action. If the
		 *          animate parameter is not specified, the behavior will use
		 *          {@link #disableAnim} to determine whether to animate or
		 *          not.
		 * @return {Ext.picker.Date} this
		 */
		hideMonthPicker: function (animate) {
			var me = this, picker = me.monthPicker;

			if (picker && picker.isVisible()) {
				if (me.shouldAnimate(animate)) {
					me.runAnimation(true);
				} else {
					picker.hide();
				}
			}
			return me;
		},

		doShowMonthPicker: function () {
			// Wrap in an extra call so we can prevent the button
			// being passed as an animation parameter.
			this.showMonthPicker();
		},

		doHideMonthPicker: function () {
			// Wrap in an extra call so we can prevent this
			// being passed as an animation parameter
			this.hideMonthPicker();
		},

		/**
		 * Show the month picker
		 * 
		 * @param {Boolean}
		 *          [animate] Indicates whether to animate this action. If the
		 *          animate parameter is not specified, the behavior will use
		 *          {@link #disableAnim} to determine whether to animate or
		 *          not.
		 * @return {Ext.picker.Date} this
		 */
		showMonthPicker: function (animate) {
			var me = this, el = me.el, picker;

			if (me.rendered && !me.disabled) {
				picker = me.createMonthPicker();
				if (!picker.isVisible()) {
					picker.setValue(me.getActive());
					picker.setSize(el.getSize());

					// Null out floatParent so that the [-1, -1] position is not
					// made relative to this
					picker.floatParent = null;
					picker.setPosition(-el.getBorderWidth('l'), -el
						.getBorderWidth('t'));
					if (me.shouldAnimate(animate)) {
						me.runAnimation(false);
					} else {
						picker.show();
					}
				}
			}
			return me;
		},

		/**
		 * Checks whether a hide/show action should animate
		 * 
		 * @private
		 * @param {Boolean}
		 *          [animate] A possible animation value
		 * @return {Boolean} Whether to animate the action
		 */
		shouldAnimate: function (animate) {
			return Ext.isDefined(animate) ? animate : !this.disableAnim;
		},

		/**
		 * Create the month picker instance
		 * 
		 * @private
		 * @return {Ext.picker.Month} picker
		 */
		createMonthPicker: function () {
			var me = this, picker = me.monthPicker;

			if (!picker) {
				me.monthPicker = picker = new Ext.picker.Month({
					renderTo: me.el,
					// We need to set the ownerCmp so that owns() can correctly
					// match up the component hierarchy so that focus does not leave
					// an owning picker field if/when this gets focus.
					ownerCmp: me,
					floating: true,
					padding: me.padding,
					shadow: false,
					small: me.showToday === false,
					footerButtonUI: me.footerButtonUI,
					listeners: {
						scope: me,
						cancelclick: me.onCancelClick,
						okclick: me.onOkClick,
						yeardblclick: me.onOkClick,
						monthdblclick: me.onOkClick
					}
				});
				if (!me.disableAnim) {
					// hide the element if we're animating to prevent an initial
					// flicker
					picker.el.setStyle('display', 'none');
				}
				picker.hide();
				me.on('beforehide', me.doHideMonthPicker, me);
			}
			return picker;
		},

		/**
		 * Respond to an ok click on the month picker
		 * 
		 * @private
		 */
		onOkClick: function (picker, value) {
			var me = this, month = value[0], year = value[1], date = new Date(
				year, month, me.getActive().getDate());

			if (date.getMonth() !== month) {
				// 'fix' the JS rolling date conversion if needed
				date = Ext.Date.getLastDateOfMonth(new Date(year, month, 1));
			}
			me.setValue(date);
			me.hideMonthPicker();
		},

		/**
		 * Respond to a cancel click on the month picker
		 * 
		 * @private
		 */
		onCancelClick: function () {
			this.selectedUpdate(this.activeDate);
			this.hideMonthPicker();
		},

		/**
		 * Show the previous month.
		 * 
		 * @param {Object}
		 *          e
		 * @return {Ext.picker.Date} this
		 */
		showPrevMonth: function (e) {
			return this.setValue(Ext.Date.add(this.activeDate, Ext.Date.MONTH,
				-1));
		},

		/**
		 * Show the next month.
		 * 
		 * @param {Object}
		 *          e
		 * @return {Ext.picker.Date} this
		 */
		showNextMonth: function (e) {
			return this.setValue(Ext.Date.add(this.activeDate, Ext.Date.MONTH,
				1));
		},

		/**
		 * Show the previous year.
		 * 
		 * @return {Ext.picker.Date} this
		 */
		showPrevYear: function () {
			return this.setValue(Ext.Date.add(this.activeDate, Ext.Date.YEAR,
				-1));
		},

		/**
		 * Show the next year.
		 * 
		 * @return {Ext.picker.Date} this
		 */
		showNextYear: function () {
			return this.setValue(Ext.Date
				.add(this.activeDate, Ext.Date.YEAR, 1));
		},

		/**
		 * Respond to the mouse wheel event
		 * 
		 * @private
		 * @param {Ext.event.Event}
		 *          e
		 */
		handleMouseWheel: function (e) {
			var delta;

			e.stopEvent();

			if (!this.disabled) {
				delta = e.getWheelDelta();

				if (delta > 0) {
					this.showPrevMonth();
				} else if (delta < 0) {
					this.showNextMonth();
				}
			}
		},

		/**
		 * Respond to a date being clicked in the picker
		 * 
		 * @private
		 * @param {Ext.event.Event}
		 *          e
		 * @param {HTMLElement}
		 *          t
		 */
		handleDateClick: function (e, t) {
			var me = this, handler = me.handler;

			e.stopEvent();

			if (!me.disabled && t.dateValue
				&& !Ext.fly(t.parentNode).hasCls(me.disabledCellCls)) {
				me.setValue(new Date(t.dateValue));
				me.fireEvent('select', me, me.value);

				if (handler) {
					handler.call(me.scope || me, me, me.value);
				}

				// event handling is turned off on hide
				// when we are using the picker in a field
				// therefore onSelect comes AFTER the select
				// event.
				me.onSelect();
			}
		},

		/**
		 * Perform any post-select actions
		 * 
		 * @private
		 */
		onSelect: function () {
			if (this.hideOnSelect) {
				this.hide();
			}
		},

		/**
		 * Sets the current value to today.
		 * 
		 * @return {Ext.picker.Date} this
		 */
		selectToday: function () {
			var me = this, btn = me.todayBtn, handler = me.handler;

			if (btn && !btn.disabled) {
				var date = new Date();
				me.hourField.setValue(date.getHours());
				me.minuteField.setValue(date.getMinutes());
				me.secondField.setValue(date.getSeconds());
				me.setValue(date);
				me.fireEvent('select', me, me.value);
				if (handler) {
					handler.call(me.scope || me, me, me.value);
				}
				me.onSelect();
			}
			return me;
		},

		/**
		 * Update the selected cell
		 * 
		 * @private
		 * @param {Date}
		 *          date The new date
		 */
		selectedUpdate: function (date) {
			var me = this, t = Ext.Date.clearTime(date, true).getTime(), cells = me.cells, cls = me.selectedCls, c, cLen = cells
				.getCount(), cell;

			me.eventEl.dom.setAttribute('aria-busy', 'true');

			cell = me.activeCell;

			if (cell) {
				Ext.fly(cell).removeCls(cls);
				cell.setAttribute('aria-selected', false);
			}

			for (c = 0; c < cLen; c++) {
				cell = cells.item(c);

				if (me.textNodes[c].dateValue === t) {
					me.activeCell = cell.dom;
					me.eventEl.dom.setAttribute('aria-activedescendant',
						cell.dom.id);
					cell.dom.setAttribute('aria-selected', true);
					cell.addCls(cls);
					me.fireEvent('highlightitem', me, cell);
					break;
				}
			}

			me.eventEl.dom.removeAttribute('aria-busy');
		},

		/**
		 * Update the contents of the picker for a new month
		 * 
		 * @private
		 * @param {Date}
		 *          date The new date
		 */
		fullUpdate: function (date) {
			var me = this, cells = me.cells.elements, textNodes = me.textNodes, disabledCls = me.disabledCellCls, eDate = Ext.Date, i = 0, extraDays = 0, newDate = +eDate
				.clearTime(date, true), today = +eDate.clearTime(new Date()), min = me.minDate ? eDate
					.clearTime(me.minDate, true)
					: Number.NEGATIVE_INFINITY, max = me.maxDate ? eDate.clearTime(
						me.maxDate, true) : Number.POSITIVE_INFINITY, ddMatch = me.disabledDatesRE, ddText = me.disabledDatesText, ddays = me.disabledDays ? me.disabledDays
							.join('')
							: false, ddaysText = me.disabledDaysText, format = me.format, days = eDate
								.getDaysInMonth(date), firstOfMonth = eDate
									.getFirstDateOfMonth(date), startingPos = firstOfMonth.getDay()
										- me.startDay, previousMonth = eDate.add(date, eDate.MONTH, -1), ariaTitleDateFormat = me.ariaTitleDateFormat, prevStart, current, disableToday, tempDate, setCellClass, html, cls, formatValue, value;

			if (startingPos < 0) {
				startingPos += 7;
			}

			days += startingPos;
			prevStart = eDate.getDaysInMonth(previousMonth) - startingPos;
			current = new Date(previousMonth.getFullYear(), previousMonth
				.getMonth(), prevStart, me.initHour);

			if (me.showToday) {
				tempDate = eDate.clearTime(new Date());
				disableToday = (tempDate < min
					|| tempDate > max
					|| (ddMatch && format && ddMatch.test(eDate.dateFormat(
						tempDate, format))) || (ddays && ddays.indexOf(tempDate
							.getDay()) !== -1));

				me.todayDisabled = disableToday;
				if (!me.disabled) {
					me.todayBtn.setDisabled(disableToday);
				}
			}

			setCellClass = function (cellIndex, cls) {
				var cell = cells[cellIndex], describedBy = [];

				// Cells are not rendered with ids
				if (!cell.hasAttribute('id')) {
					cell.setAttribute('id', me.id + '-cell-' + cellIndex);
				}

				// store dateValue number as an expando
				value = +eDate.clearTime(current, true);
				cell.firstChild.dateValue = value;

				cell.setAttribute('aria-label', eDate.format(current,
					ariaTitleDateFormat));

				// Here and below we can't use title attribute instead of
				// data-qtip
				// because JAWS will announce title value before cell content
				// which is not what we need. Also we are using aria-describedby
				// attribute
				// and not placing the text in aria-label because some cells may
				// have
				// compound descriptions (like Today and Disabled day).
				cell.removeAttribute('aria-describedby');
				cell.removeAttribute('data-qtip');

				if (value === today) {
					cls += ' ' + me.todayCls;
					describedBy.push(me.id + '-todayText');
				}

				if (value === newDate) {
					me.activeCell = cell;
					me.eventEl.dom.setAttribute('aria-activedescendant', cell.id);
					cell.setAttribute('aria-selected', true);
					cls += ' ' + me.selectedCls;
					me.fireEvent('highlightitem', me, cell);
				} else {
					cell.setAttribute('aria-selected', false);
				}

				if (value < min) {
					cls += ' ' + disabledCls;
					describedBy.push(me.id + '-ariaMinText');
					cell.setAttribute('data-qtip', me.minText);
				} else if (value > max) {
					cls += ' ' + disabledCls;
					describedBy.push(me.id + '-ariaMaxText');
					cell.setAttribute('data-qtip', me.maxText);
				} else if (ddays && ddays.indexOf(current.getDay()) !== -1) {
					cell.setAttribute('data-qtip', ddaysText);
					describedBy.push(me.id + '-ariaDisabledDaysText');
					cls += ' ' + disabledCls;
				} else if (ddMatch && format) {
					formatValue = eDate.dateFormat(current, format);
					if (ddMatch.test(formatValue)) {
						cell.setAttribute('data-qtip', ddText.replace('%0',
							formatValue));
						describedBy.push(me.id + '-ariaDisabledDatesText');
						cls += ' ' + disabledCls;
					}
				}

				if (describedBy.length) {
					cell.setAttribute('aria-describedby', describedBy.join(' '));
				}

				cell.className = cls + ' ' + me.cellCls;
			};

			me.eventEl.dom.setAttribute('aria-busy', 'true');

			for (; i < me.numDays; ++i) {
				if (i < startingPos) {
					html = (++prevStart);
					cls = me.prevCls;
				} else if (i >= days) {
					html = (++extraDays);
					cls = me.nextCls;
				} else {
					html = i - startingPos + 1;
					cls = me.activeCls;
				}
				textNodes[i].innerHTML = html;
				current.setDate(current.getDate() + 1);
				setCellClass(i, cls);
			}

			me.eventEl.dom.removeAttribute('aria-busy');

			me.monthBtn.setText(Ext.Date.format(date, me.monthYearFormat));

			me.hourField.setValue(date.getHours());
			me.minuteField.setValue(date.getMinutes());
			me.secondField.setValue(date.getSeconds());

		},

		/**
		 * Update the contents of the picker
		 * 
		 * @private
		 * @param {Date}
		 *          date The new date
		 * @param {Boolean}
		 *          forceRefresh True to force a full refresh
		 */
		update: function (date, forceRefresh) {
			var me = this, active = me.activeDate;
			me.hourField.setValue(date.getHours());
			me.minuteField.setValue(date.getMinutes());
			me.secondField.setValue(date.getSeconds());
			if (me.rendered) {
				me.activeDate = date;
				if (!forceRefresh && active && me.el
					&& active.getMonth() === date.getMonth()
					&& active.getFullYear() === date.getFullYear()) {
					me.selectedUpdate(date, active);
				} else {
					me.fullUpdate(date, active);
				}
			}
			return me;
		},

		/**
		 * @private
		 * @inheritdoc
		 */
		beforeDestroy: function () {
			var me = this;
			if (me.rendered) {
				Ext.destroy(me.keyNav, me.monthPicker, me.monthBtn,
					me.nextRepeater, me.prevRepeater, me.todayBtn, me.cleanBtn,
					me.todayElSpan, me.hourField, me.minuteField, me.secondField,
					me.okBtn);
				delete me.textNodes;
				delete me.cells.elements;
			}
			me.callParent();
		},

		privates: {
			// Do the job of a container layout at this point even though we are
			// not a Container.
			// TODO: Refactor as a Container.
			finishRenderChildren: function () {
				var me = this;

				me.callParent();
				me.monthBtn.finishRender();

				me.hourField.finishRender();
				me.minuteField.finishRender();
				me.secondField.finishRender();

				if (me.showToday) {
					me.okBtn.finishRender();
					me.todayBtn.finishRender();
					// add by wonght
					me.cleanBtn.finishRender();
				}
			},

			getFocusEl: function () {
				return this.eventEl;
			},

			/**
			 * Set the disabled state of various internal components
			 * 
			 * @param {Boolean}
			 *          disabled
			 * @private
			 */
			syncDisabled: function (disabled, doButton) {
				var me = this, keyNav = me.keyNav, todayBtn = me.todayBtn;

				// If we have one, we have all
				if (keyNav) {
					keyNav.setDisabled(disabled);
					me.prevRepeater.setDisabled(disabled);
					me.nextRepeater.setDisabled(disabled);
				}
				if (doButton && todayBtn) {
					todayBtn.setDisabled(me.todayDisabled || disabled);
				}
			}
		}
	});

// Ext.define("Ext.locale.zh_CN.picker.DateTime", {
// 	override: "appfe.ux.picker.DateTime",
// 	todayText: "现在",
// 	minText: "日期必须大于最小允许日期",
// 	// update
// 	maxText: "日期必须小于最大允许日期",
// 	// update
// 	disabledDaysText: "",
// 	disabledDatesText: "",
// 	nextText: '下个月 (Ctrl+Right)',
// 	prevText: '上个月 (Ctrl+Left)',
// 	monthYearText: '选择一个月 (Ctrl+Up/Down 来改变年份)',
// 	// update
// 	todayTip: "{0}",
// 	format: "y年m月d日",
// 	ariaTitle: '{0}',
// 	ariaTitleDateFormat: 'Y\u5e74m\u6708d\u65e5',
// 	longDayFormat: 'Y\u5e74m\u6708d\u65e5',
// 	monthYearFormat: 'Y\u5e74m\u6708',
// 	getDayInitial: function (value) {
// 		// Grab the last character
// 		return value.substr(value.length - 1);
// 	}
// });
